<template>
  <teleport :to="getContainer || 'body'">
    <div ref="modalWrapRef" class="draggable-modal" :class="{ fullscreen: fullscreenModel }">
      <Modal
        v-bind="omit(props, ['open', 'onCancel', 'onOk', 'onUpdate:open'])"
        v-model:open="openModel"
        :mask-closable="false"
        :get-container="() => modalWrapRef"
        :width="innerWidth"
        @ok="emit('ok')"
        @cancel="emit('cancel')"
      >
        <template #title>
          <slot name="title">{{ $attrs.title || '标题' }}</slot>
        </template>
        <template #closeIcon>
          <slot name="closeIcon">
            <Space v-if="showFullScreenIcon" class="ant-modal-operate" @click.stop>
              <FullscreenOutlined v-if="!fullscreenModel" @click="fullscreenModel = true" />
              <FullscreenExitOutlined v-else @click="restore" />
              <CloseOutlined v-if="showCloseIcon" @click="closeModal" />
            </Space>
            <CloseOutlined v-if="showCloseIcon" @click="closeModal" />
          </slot>
        </template>
        <slot>
          ① 窗口可以拖动；<br />
          ② 窗口可以通过八个方向改变大小；<br />
          ③ 窗口可以最小化、最大化、还原、关闭；<br />
          ④ 限制窗口最小宽度/高度。
        </slot>
        <template v-if="$slots.footer" #footer>
          <slot name="footer" />
        </template>
      </Modal>
    </div>
  </teleport>
</template>

<script setup>
import { ref, watch, nextTick, defineModel } from 'vue'
import { useRoute } from 'vue-router'
import { modalProps } from 'ant-design-vue/es/modal/Modal'
import { CloseOutlined, FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons-vue'
import { throttle, omit } from 'lodash-es'
import { Modal, Space } from 'ant-design-vue'

const props = defineProps({
  ...modalProps(),
  fullscreen: {
    type: Boolean,
    default: false
  }
})

const emit = defineEmits(['update:open', 'update:fullscreen', 'ok', 'cancel'])

const route = useRoute()
const openModel = defineModel('open')
const resizeModal = ref(props.resizeModal) // 是否支持窗口可以通过八个方向改变大小
const fullscreenModel = ref(props.fullscreen) // 全屏-小屏 flag字段
const showFullScreenIcon = ref(props.showFullScreenIcon) // 是否显示大小屏功能
const showCloseIcon = ref(props.showCloseIcon) // 是否显示右上角X关闭功能功能
const innerWidth = ref(props.width || 880)
const cursorStyle = {
  top: 'n-resize',
  left: 'w-resize',
  right: 'e-resize',
  bottom: 's-resize',
  topLeft: 'nw-resize',
  topright: 'ne-resize',
  bottomLeft: 'sw-resize',
  bottomRight: 'se-resize',
  auto: 'auto'
}

// 是否已经初始化过了
let inited = false
const modalWrapRef = ref()

const closeModal = () => {
  openModel.value = false
  emit('cancel')
}

// 居中弹窗
const centerModal = async () => {
  await nextTick()
  const modalEl = modalWrapRef.value?.querySelector('.ant-modal')

  if (modalEl && modalEl.getBoundingClientRect().left < 1) {
    modalEl.style.left = `${(document.documentElement.clientWidth - modalEl.offsetWidth) / 2}px`
  }
}

const restore = async () => {
  fullscreenModel.value = false
  centerModal()
}

const registerDragTitle = (dragEl, handleEl) => {
  handleEl.style.cursor = 'move'
  handleEl.onmousedown = throttle((e) => {
    if (fullscreenModel.value) return
    document.body.style.userSelect = 'none'
    const disX = e.clientX - dragEl.getBoundingClientRect().left
    const disY = e.clientY - dragEl.getBoundingClientRect().top
    const mousemove = (event) => {
      if (fullscreenModel.value) return
      let iL = event.clientX - disX
      let iT = event.clientY - disY
      const maxL = document.documentElement.clientWidth - dragEl.offsetWidth
      const maxT = document.documentElement.clientHeight - dragEl.offsetHeight

      iL <= 0 && (iL = 0)
      iT <= 0 && (iT = 0)
      iL >= maxL && (iL = maxL)
      iT >= maxT && (iT = maxT)

      dragEl.style.left = `${Math.max(iL, 0)}px`
      dragEl.style.top = `${Math.max(iT, 0)}px`
    }
    const mouseup = () => {
      document.removeEventListener('mousemove', mousemove)
      document.removeEventListener('mouseup', mouseup)
      document.body.style.userSelect = 'auto'
    }

    document.addEventListener('mousemove', mousemove)
    document.addEventListener('mouseup', mouseup)
  }, 20)
}

const initDrag = async () => {
  await nextTick()
  const modalWrapRefEl = modalWrapRef.value
  const modalWrapEl = modalWrapRefEl.querySelector('.ant-modal-wrap')
  const modalEl = modalWrapRefEl.querySelector('.ant-modal')
  if (modalWrapEl && modalEl) {
    centerModal()
    const headerEl = modalEl.querySelector('.ant-modal-header')
    headerEl && registerDragTitle(modalEl, headerEl)

    modalWrapEl.onmousemove = throttle((event) => {
      if (fullscreenModel.value) return
      // 是否支持窗口可以通过八个方向改变大小
      if (!resizeModal.value) return
      const left = event.clientX - modalEl.offsetLeft
      const top = event.clientY - modalEl.offsetTop
      const right = event.clientX - modalEl.offsetWidth - modalEl.offsetLeft
      const bottom = event.clientY - modalEl.offsetHeight - modalEl.offsetTop
      const isLeft = left <= 0 && left > -8
      const isTop = top < 5 && top > -8
      const isRight = right >= 0 && right < 8
      const isBottom = bottom > -5 && bottom < 8
      // 向左
      if (isLeft && top > 5 && bottom < -5) {
        modalWrapEl.style.cursor = cursorStyle.left
        // 向上
      } else if (isTop && left > 5 && right < -5) {
        modalWrapEl.style.cursor = cursorStyle.top
        // 向右
      } else if (isRight && top > 5 && bottom < -5) {
        modalWrapEl.style.cursor = cursorStyle.right
        // 向下
      } else if (isBottom && left > 5 && right < -5) {
        modalWrapEl.style.cursor = cursorStyle.bottom
        // 左上角
      } else if (left > -8 && left <= 5 && top <= 5 && top > -8) {
        modalWrapEl.style.cursor = cursorStyle.topLeft
        // 左下角
      } else if (left > -8 && left <= 5 && bottom <= 5 && bottom > -8) {
        modalWrapEl.style.cursor = cursorStyle.bottomLeft
        // 右上角
      } else if (right < 8 && right >= -5 && top <= 5 && top > -8) {
        modalWrapEl.style.cursor = cursorStyle.topright
        // 右下角
      } else if (right < 8 && right >= -5 && bottom <= 5 && bottom > -8) {
        modalWrapEl.style.cursor = cursorStyle.bottomRight
      } else {
        modalWrapEl.style.cursor = cursorStyle.auto
      }
    }, 20)
    modalWrapEl.onmousedown = (e) => {
      if (fullscreenModel.value) return
      // 是否支持窗口可以通过八个方向改变大小
      if (!resizeModal.value) return
      const {
        top: iParentTop,
        bottom: iParentBottom,
        left: iParentLeft,
        right: iParentRight
      } = modalEl.getBoundingClientRect()

      const disX = e.clientX - iParentLeft
      const disY = e.clientY - iParentTop
      const iParentWidth = modalEl.offsetWidth
      const iParentHeight = modalEl.offsetHeight

      const cursor = modalWrapEl.style.cursor

      const mousemove = throttle((event) => {
        if (fullscreenModel.value) return
        if (cursor !== cursorStyle.auto) {
          document.body.style.userSelect = 'none'
        }
        const mLeft = `${Math.max(0, event.clientX - disX)}px`
        const mTop = `${Math.max(0, event.clientY - disY)}px`
        const mLeftWidth = `${Math.min(iParentRight, iParentWidth + iParentLeft - event.clientX)}px`
        const mRightWidth = `${Math.min(
          window.innerWidth - iParentLeft,
          event.clientX - iParentLeft
        )}px`
        const mTopHeight = `${Math.min(
          iParentBottom,
          iParentHeight + iParentTop - event.clientY
        )}px`
        const mBottomHeight = `${Math.min(
          window.innerHeight - iParentTop,
          event.clientY - iParentTop
        )}px`

        // 向左边拖拽
        if (cursor === cursorStyle.left) {
          modalEl.style.left = mLeft
          modalEl.style.width = mLeftWidth
          // 向上边拖拽
        } else if (cursor === cursorStyle.top) {
          modalEl.style.top = mTop
          modalEl.style.height = mTopHeight
          // 向右边拖拽
        } else if (cursor === cursorStyle.right) {
          modalEl.style.width = mRightWidth
          // 向下拖拽
        } else if (cursor === cursorStyle.bottom) {
          modalEl.style.height = mBottomHeight
          // 左上角拖拽
        } else if (cursor === cursorStyle.topLeft) {
          modalEl.style.left = mLeft
          modalEl.style.top = mTop
          modalEl.style.height = mTopHeight
          modalEl.style.width = mLeftWidth
          // 右上角拖拽
        } else if (cursor === cursorStyle.topright) {
          modalEl.style.top = mTop
          modalEl.style.width = mRightWidth
          modalEl.style.height = mTopHeight
          // 左下角拖拽
        } else if (cursor === cursorStyle.bottomLeft) {
          modalEl.style.left = mLeft
          modalEl.style.width = mLeftWidth
          modalEl.style.height = mBottomHeight
          // 右下角拖拽
        } else if (cursor === cursorStyle.bottomRight) {
          modalEl.style.width = mRightWidth
          modalEl.style.height = mBottomHeight
        }
        innerWidth.value = modalEl.style.width
      }, 20)

      const mouseup = () => {
        document.removeEventListener('mousemove', mousemove)
        document.removeEventListener('mouseup', mouseup)
        document.body.style.userSelect = 'auto'
        modalWrapEl.style.cursor = cursorStyle.auto
      }

      document.addEventListener('mousemove', mousemove)
      document.addEventListener('mouseup', mouseup)
    }
  }
  inited = true
}

watch(openModel, async (val) => {
  if ((val && Object.is(inited, false)) || props.destroyOnClose) {
    initDrag()
  }
})

watch(() => route.fullPath, closeModal)
</script>

<style lang="scss">
.draggable-modal {
  &.fullscreen {
    .ant-modal {
      inset: 0 !important;
      width: 100% !important;
      max-width: 100vw !important;
      height: 100% !important;
    }

    .ant-modal-content {
      width: 100% !important;
      height: 100% !important;
    }
  }

  .ant-modal-wrap {
    overflow-x: hidden;
  }

  .ant-modal {
    position: relative;
    min-width: 200px;
    min-height: 200px;
    margin: 0;
    padding: 0;

    .ant-modal-header {
      user-select: none;
      .ant-modal-title {
        font-weight: bold;
        font-size: 18px;
        color: #333333;
      }
    }

    .ant-modal-close {
      top: 15px;
      // right: 30px;
      background-color: transparent;
      cursor: inherit;

      &:hover,
      &:focus {
        color: rgb(0 0 0 / 45%);
      }

      .ant-space-item:hover .anticon,
      .ant-space-item:focus .anticon {
        color: rgb(0 0 0 / 75%);
        text-decoration: none;
      }

      .ant-modal-close-x {
        // width: 50px;
        // height: 50px;
        // line-height: 44px;

        .ant-space {
          width: 100%;
          height: 100%;
        }
      }
    }

    .ant-modal-content {
      /* width: ~'v-bind("props.width")px'; */
      display: flex;
      flex-direction: column;
      width: 100%;
      min-width: 200px;
      height: 100%;
      min-height: 200px;
      padding-top: 0;
      overflow: hidden;
      padding: 0 !important;
      .ant-modal-header {
        height: 50px;
        display: flex;
        padding: 0 24px;
        align-items: center;
        background: #ebeff4;
      }

      .ant-modal-body {
        padding: 0 24px;
        flex: auto;
        height: 100%;
        // overflow: hidden;
      }
      .ant-modal-footer {
        padding: 12px 24px 12px;
        margin: 0;
      }
    }
  }
}
</style>
